package org.p2psimulator.entity.protocol.structured.chordcycles;

import java.util.List;
import org.p2psimulator.entity.Message;
import org.p2psimulator.entity.Peer;
import org.p2psimulator.entity.overlaynetwork.ChordOverlay;
import org.p2psimulator.entity.overlaynetwork.routingtable.ChordFingerTable;
import org.p2psimulator.entity.protocol.messages.MessageTypes;
import org.p2psimulator.entity.protocol.messages.chord.FindSucessor;
import org.p2psimulator.entity.protocol.messages.chord.NewPredecessor;
import org.p2psimulator.entity.protocol.messages.chord.SucessorHere;
import org.p2psimulator.engine.Environment;
import org.p2psimulator.util.SystemLogger;

public class ChordJoinCycle extends ChordCycle {

	public ChordJoinCycle(Environment env, int transId) {
		super(env, transId);
	}

	@Override
	public String runRequestCycle(Peer p) {
		StringBuilder str = new StringBuilder("   [JOIN CYCLE]   -   ");
		if (!p.isOnline()) {
			/**
			 * Para entrar na rede um peer p deve primeiro conhecer um peer k
			 * que já esteja na rede. Como não há servidores específicos para
			 * ingresso na rede, simulamos este comportamento fazendo com que o
			 * peer escolha um host aleatório do sistema.
			 */
			ChordOverlay overlay = (ChordOverlay) env
					.getChordOverlayNetwork();
			Peer k = overlay.getOnlineRandomPeer();

			if (k == null) {
				SystemLogger.transactionLogger
						.error("Nenhum peer foi encontrado na rede");
				return "";
			}
			/**
			 * O peer 'p' pede então ao peer escolhido 'k' que retorne o id so
			 * seu sucessor na rede. Após receber o id do sucessor 's', 'p'
			 * informa a 's' que ele agora é seu predecessor
			 */
			FindSucessor findSucessor = new FindSucessor(p.getId(), k.getId(),
					this.currentTransactionId, p.getBehavior().getBehaviorType());

			findSucessor.setTTL(15);
			findSucessor.setHOP(0);
			findSucessor.setServentId(p.getId());
			findSucessor.setReferredId(p.getId());

			env.getNetwork().sendMessage(findSucessor);
			str.append("[ ESTAVA NA REDE: false ]");
			str.append(" [ FINDSUCESSOR ENVIADA:  de:" + p.getId() + "; para: "
					+ k.getId() + " ]");
		} else {
			str.append("[ ESTAVA NA REDE: true ]");
		}

		return str.toString();
	}

	@Override
	public String runResponseCycle(Peer p) {
		StringBuilder str = new StringBuilder("   [JOIN CYCLE]   -   ");
		/**
		 * Uma vez recebido o Id de 's' sucessor de 'p', este processegue o
		 * processo de entrada na rede informando a 's' que é o seu mais novo
		 * predecessor
		 */
		List<Message> sucessorList = p.getMessageStack().get(
				MessageTypes.CHORD_SUCESSORHERE.getName());
		if (sucessorList != null && sucessorList.size() > 0) {

			str.append(" [ TOTAL DE SUCESSORES RECEBIDOS: "
					+ sucessorList.size() + " ]");
			SucessorHere sucessorHere = (SucessorHere) sucessorList.get(0);
			// autaliza os ponteiros de sucessor e predecessor em sua finger
			// table
			ChordFingerTable table = getFingerTable(p);
			table.setSucessor(sucessorHere.getSucessorId());
			table.setOwner(p.getId());

			// Informa ao sucessor que ele agora é seu predecessor
			NewPredecessor newPredecessor = new NewPredecessor(p.getId(),
					sucessorHere.getSucessorId(), this.currentTransactionId, p
							.getBehavior().getBehaviorType());
			newPredecessor.setTTL(255);
			newPredecessor.setHOP(0);
			newPredecessor.setServentId(p.getId());
			env.getNetwork().sendMessage(newPredecessor);

			p.setOnline(true);
			str.append("[ SUCESSOR ENCONTRADO: [" + sucessorHere.getServentId()
					+ " ] ]");
		} else {
			p.setOnline(false);
			str.append("[ SUCESSOR ENCONTRADO: [ ] : error ]");
			str.append(" [ TOTAL DE SUCESSORES RECEBIDOS: 0 ]");
		}

		return str.toString();
	}

	@Override
	public String runSecondRequestCycle(Peer p) {
		// TODO Auto-generated method stub
		return "";
	}

	@Override
	public String runSecondResponseCycle(Peer p) {
		// TODO Auto-generated method stub
		return "";
	}

}
